package a.b.mvc6.config;

import com.alibaba.druid.DbType;
import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.extension.incrementer.PostgreKeyGenerator;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.pagination.dialects.PostgreDialect;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.Collections;
import java.util.List;

@Configuration
@MapperScan(basePackages = "a.b.mvc6.mapper")
@EnableTransactionManagement
public class MybatisPlusConfig {

    @Autowired
    private Environment env;

    @Bean(initMethod = "init", destroyMethod = "close")
    public DruidDataSource dataSource() throws SQLException {
        DruidDataSource dataSource = new DruidDataSource();
        // 常见配置
        dataSource.setDriverClassName(env.getProperty("jdbc.driver-class-name", "org.postgresql.Driver"));
        dataSource.setUrl(env.getProperty("jdbc.url", "jdbc:postgresql://106.13.40.185:5432/demo"));
        dataSource.setUsername(env.getProperty("jdbc.username", "supabase_admin"));
        dataSource.setPassword(env.getProperty("jdbc.password", "liang1997"));

        // 连接池配置
        dataSource.setMaxActive(Integer.parseInt(env.getProperty("druid.max-active", "20"))); // 最大线程数
        dataSource.setInitialSize(Integer.parseInt(env.getProperty("druid.initial-size", "1"))); // 初始化数据源时建立物理连接的个数
        dataSource.setMaxWait(Long.parseLong(env.getProperty("druid.max-wait", "6000"))); // 连接最大等待时间 毫秒
        dataSource.setMinIdle(Integer.parseInt(env.getProperty("druid.min-idle", "1"))); // 最小核心线程

        dataSource.setTimeBetweenEvictionRunsMillis(60000); // 申请连接的时候检测，如果空闲时间大于timeBetweenEvictionRunsMillis，执行validationQuery检测连接是否有效 毫秒
        dataSource.setMinEvictableIdleTimeMillis(300000); // 连接保持空闲而不被驱逐的最小时间 毫秒
        dataSource.setValidationQuery("select 1");

        dataSource.setTestWhileIdle(Boolean.TRUE); // 如果空闲时间大于timeBetweenEvictionRunsMillis，执行validationQuery检测连接是否有效
        dataSource.setTestOnBorrow(Boolean.TRUE); // 申请连接时执行validationQuery检测连接是否有效 影响性能
        dataSource.setTestOnReturn(Boolean.FALSE); // 归还连接时执行validationQuery检测连接是否有效 影响性能

        dataSource.setPoolPreparedStatements(Boolean.FALSE); // 是否缓存preparedStatement
        dataSource.setMaxPoolPreparedStatementPerConnectionSize(20); // 每个连接大小的最大池准备语句


        dataSource.setAsyncInit(Boolean.FALSE); // 如果有initialSize数量较多时，打开会加快应用启动时间

        // 不常见配置
        // 属性类型是逗号隔开的字符串，通过别名的方式配置扩展插件，插件别名列表请参考druid jar包中的 /META-INF/druid-filter.properties
        dataSource.setFilters("stat,slf4j");


        StatFilter statFilter = new StatFilter();
        statFilter.setSlowSqlMillis(1); // 2秒就算慢SQL
        statFilter.setLogSlowSql(true); // 记录慢速 SQL
        statFilter.setMergeSql(true);
        statFilter.setSlowSqlLogLevel("INFO");
        statFilter.setDbType(DbType.postgresql);
        dataSource.setProxyFilters(List.of(statFilter));

        return dataSource;
    }

    @Bean
    public SqlSessionFactory sqlSessionFactory(@Autowired DataSource dataSource) throws Exception {
        MybatisSqlSessionFactoryBean factoryBean = new MybatisSqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);

        // 指定实体类所在包，自动别名
        factoryBean.setTypeAliasesPackage("a.b.mvc6.entity");
        factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/**/*.xml"));


        // Mybatis Plus 全局配置
        // TODO 在这里扩展Mybatis Plus 的全局配置
        GlobalConfig globalConfig = new GlobalConfig();

        // TODO 在这里扩展Mybatis Plus 的DB配置
        GlobalConfig.DbConfig dbConfig = new GlobalConfig.DbConfig();
        // 设置主键生成器
        dbConfig.setKeyGenerators(Collections.singletonList(new PostgreKeyGenerator()));

        globalConfig.setDbConfig(dbConfig);
        factoryBean.setGlobalConfig(globalConfig);



        // 设置MyBatisPlus 拦截器
        // TODO 在这里扩展Mybatis Plus 的 拦截器
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        // 分页 拦截器
        PaginationInnerInterceptor pageInnerInterceptor = new PaginationInnerInterceptor();
        pageInnerInterceptor.setDbType(com.baomidou.mybatisplus.annotation.DbType.POSTGRE_SQL);
        pageInnerInterceptor.setDialect(new PostgreDialect());
        // 乐观锁 拦截器 当为True时Wrapper也使用乐观锁  在实体类的字段中使用 @Version 注解
        OptimisticLockerInnerInterceptor lockerInnerInterceptor = new OptimisticLockerInnerInterceptor(Boolean.TRUE);
        // 防止全表更新与删除 拦截器
        BlockAttackInnerInterceptor attackInnerInterceptor = new BlockAttackInnerInterceptor();
        mybatisPlusInterceptor.setInterceptors(List.of(pageInnerInterceptor, lockerInnerInterceptor, attackInnerInterceptor));
        // 设置插件
        factoryBean.setPlugins(mybatisPlusInterceptor);

        factoryBean.afterPropertiesSet();
        return factoryBean.getObject();
    }

    /**
     * 配置数据源事务管理器
     * @param dataSource 数据源
     * @return DataSourceTransactionManager
     */
    @Bean
    public DataSourceTransactionManager transactionManager(@Autowired DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
}
